////////////////////////////////////////////////////////////////////////////////
//
//  based on Adobe source
//
//  link:
//
//  http://opensource.adobe.com/svn/opensource/flex/sdk/tags/4.0.0.4021/frameworks/projects/flex4/src/mx/graphics/Parser.as
//
// enhancement by suspendmode@gmail.com
//
//
////////////////////////////////////////////////////////////////////////////////

package com.google.code.fxg.schema
{
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	
	import com.google.code.fxg.FXGDecoder;
	
	import mx.geom.Transform;
	import mx.graphics.BitmapFill;
	import mx.graphics.GradientEntry;
	import mx.graphics.LinearGradient;
	import mx.graphics.LinearGradientStroke;
	import mx.graphics.RadialGradient;
	import mx.graphics.RadialGradientStroke;
	import mx.graphics.SolidColor;
	import mx.graphics.SolidColorStroke;
	
	import spark.components.Group;
	import spark.components.RichText;
	import spark.filters.BevelFilter;
	import spark.filters.BlurFilter;
	import spark.filters.ColorMatrixFilter;
	import spark.filters.DropShadowFilter;
	import spark.filters.GlowFilter;
	import spark.filters.GradientBevelFilter;
	import spark.filters.GradientGlowFilter;
	import spark.primitives.*;

	public class FXGSchema
	{		
		////////////////////////////////////////////////////////////////////////////////
		//
		//  Helper constants: FXG schema
		//
		////////////////////////////////////////////////////////////////////////////////
		
		public static const NUMBER:int = 0;
		public static const IDENTIFIER:int = 1;
		public static const COLOR:int = 2;
		public static const STRING:int = 3;
		public static const BOOLEAN:int = 4;
		public static const EMBED:int = 5;
		public static const IGNORE:int = 6;
		public static const NUMBERLIST:int = 7;
		
		//--------------------------------------
		// standard attributes
		//--------------------------------------
		
		public static const transformAttributes:Object =
			{
				x: NUMBER,
				y: NUMBER,
				rotation: NUMBER,
				scaleX: NUMBER,
				scaleY: NUMBER,
				transformX: NUMBER,
				transformY: NUMBER
			};
		
		public static const sizeAttributes:Object =
			{
				width: NUMBER,
				height: NUMBER
			};
		
		public static const renderAttributes:Object =
			{
				alpha: NUMBER,
				blendMode: STRING,
				visible: BOOLEAN,
				maskType: STRING
			};
		
		//--------------------------------------
		// transforms
		//--------------------------------------
		
		// <Matrix>
		
		public static const matrixType:ObjectElement = new ObjectElement(
			"Matrix", Matrix,
			{
				a: NUMBER,
				b: NUMBER,
				c: NUMBER,
				d: NUMBER,
				tx: NUMBER,
				ty: NUMBER
			});
		
		// <ColorTransform>
		
		public static const colorMatrixType:ObjectElement = new ObjectElement(
			"ColorTransform", ColorTransform,
			{
				redMultiplier: NUMBER,
				greenMultiplier: NUMBER,
				blueMultiplier: NUMBER,
				alphaMultiplier: NUMBER,
				redOffset: NUMBER,
				blueOffset: NUMBER,
				greenOffset: NUMBER,
				alphaOffset: NUMBER
			});
		
		// <Transform>
		
		public static const transformType:ObjectElement = new ObjectElement(
			"Transform", Transform,
			{
			},
			{
				matrix: matrixType,
				colorTransform: ColorTransform
			});
		
		//--------------------------------------
		// fills
		//--------------------------------------
		
		// <SolidColor>
		
		public static const solidColorType:ObjectElement = new ObjectElement(
			"SolidColor", SolidColor,
			{
				color: COLOR,
				alpha: NUMBER
			});
		
		// <GradientEntry>
		
		public static const gradientEntryType:ObjectElement = new ObjectElement(
			"GradientEntry", GradientEntry,
			{
				alpha: NUMBER,
				color: COLOR,
				ratio: NUMBER
			});
		
		public static const linearGradientAttributes:Object =
			{
				spreadMethod: STRING,
				interpolationMethod: STRING,
				x: NUMBER,
				y: NUMBER,
				scaleX: NUMBER,
				rotation: NUMBER         
			};
		
		public static const radialGradientAttributes:Object =
			{
				spreadMethod: STRING,
				interpolationMethod: STRING,
				focalPointRatio: NUMBER,
				x: NUMBER,
				y: NUMBER,
				scaleX: NUMBER,
				scaleY: NUMBER,
				rotation: NUMBER
			}
		
		public static const gradientChildElements:Object =
			{
				matrix: matrixType  
			};
		
		public static const gradientEntryGroup:ObjectElementGroup = new ObjectElementGroup(
			[
				gradientEntryType
			]);
		
		// <LinearGradient>
		
		public static const linearGradientFillType:ObjectElement = new ObjectElement(
			"LinearGradient", LinearGradient,
			linearGradientAttributes,
			gradientChildElements,
			"entries",
			gradientEntryGroup);
		
		// <RadialGradient>
		
		public static const radialGradientFillType:ObjectElement = new ObjectElement(
			"RadialGradient", RadialGradient,
			radialGradientAttributes,
			gradientChildElements,
			"entries",
			gradientEntryGroup);
		
		// <BitmapFill>
		
		public static const bitmapFillType:ObjectElement = new ObjectElement(
			"BitmapFill", BitmapFill,
			{
				x: NUMBER,
				y: NUMBER,
				scaleX: NUMBER,
				scaleY: NUMBER,
				rotation: NUMBER,
				transformCenterX: NUMBER,
				transformCenterY: NUMBER,
				repeat: BOOLEAN,
				source: EMBED
			});
		
		public static const fillTypes:ObjectElementGroup = new ObjectElementGroup(
			[
				solidColorType,
				linearGradientFillType,
				radialGradientFillType,
				bitmapFillType
			]);
		
		//--------------------------------------
		// strokes
		//--------------------------------------
		
		public static const strokeAttributes:Object =
			{
				scaleMode: STRING,
				caps: STRING,
				weight: NUMBER,
				pixelHinting: BOOLEAN,
				joints: STRING,
				miterLimit: NUMBER
			};
		
		// <SolidColorStroke>
		
		public static const solidStrokeType:ObjectElement = new ObjectElement(
			"SolidColorStroke", SolidColorStroke,
			[
				{
					color: COLOR,
					alpha: NUMBER
				},
				strokeAttributes
			]);
		
		// <LinearGradientStroke>
		
		public static const linearGradientStrokeType:ObjectElement = new ObjectElement(
			"LinearGradientStroke", LinearGradientStroke,
			[
				linearGradientAttributes,
				strokeAttributes
			],
			gradientChildElements,
			"entries",
			new ObjectElementGroup([ gradientEntryType ]));
		
		// <RadialGradientStroke>
		
		public static const radialGradientStrokeType:ObjectElement = new ObjectElement(
			"RadialGradientStroke", RadialGradientStroke,
			[
				radialGradientAttributes,
				strokeAttributes
			],
			gradientChildElements,
			"entries",
			new ObjectElementGroup([ gradientEntryType ]));
		
		public static const strokeTypes:ObjectElementGroup = new ObjectElementGroup(
			[
				solidStrokeType,
				linearGradientStrokeType,
				radialGradientStrokeType
			]);
		
		public static const fillChildElements:Object = 
			{
				fill: fillTypes,
				stroke: strokeTypes
			}
		
		//--------------------------------------
		// filters
		//--------------------------------------
		
		public static const dropShadowFilterType:ObjectElement = new ObjectElement(
			"DropShadowFilter", DropShadowFilter,
			{
				alpha: NUMBER,
				angle: NUMBER,
				blurX: NUMBER,
				blurY: NUMBER,
				color: COLOR,
				distance: NUMBER,
				quality: NUMBER,
				hideObject: BOOLEAN,
				inner: BOOLEAN,
				knockout: BOOLEAN,
				strength: NUMBER
			});
		
		public static const blurFilterType:ObjectElement = new ObjectElement(
			"BlurFilter", BlurFilter,
			{
				blurX: NUMBER,
				blurY: NUMBER,
				quality: NUMBER
			});
		
		public static const bevelAttributes:Object =
			{
				blurX: NUMBER,
				blurY: NUMBER,
				quality: NUMBER,
				angle: NUMBER,
				distance: NUMBER,
				knockout: BOOLEAN,
				strength: NUMBER,
				type: STRING 
			};
		
		public static const gradientFilterAttributes:Object =
			{
				angle: NUMBER,
				blurX: NUMBER,
				blurY: NUMBER,
				distance: NUMBER,
				knockout: BOOLEAN,
				quality: NUMBER,
				strength: NUMBER,
				type: STRING
			};
		
		public static const bevelFilterType:ObjectElement = new ObjectElement(
			"BevelFilter", BevelFilter,
			[
				bevelAttributes,
				{
					highlightAlpha: NUMBER,
					highlightColor: COLOR,
					shadowAlpha: NUMBER,
					shadowColor: COLOR
				}
			]);
		
		//todo: need a filter type that support mxml style gradient entries.
		
		public static const gradientBevelFilterType:ObjectElement = new ObjectElement(
			"GradientBevelFilter", GradientBevelFilter,
			gradientFilterAttributes,
			{},
			"entries",
			gradientEntryGroup);
		
		public static const glowAttributes:Object =
			{
				blurX: NUMBER,
				blurY: NUMBER,
				quality: NUMBER,
				inner: BOOLEAN,
				knockout: BOOLEAN,
				strength: NUMBER
			};
		
		public static const glowFilterType:ObjectElement = new ObjectElement(
			"GlowFilter", GlowFilter,
			[
				glowAttributes,
				{
					color: COLOR,
					alpha:NUMBER
				}
			]);
		
		public static const colorMatrixFilterType:ObjectElement = new ObjectElement(
			"ColorMatrixFilter",ColorMatrixFilter,
			{
				matrix: NUMBERLIST
			});
		
		//todo: need a filter type that support mxml style gradient entries.
		
		public static const gradientGlowFilterType:ObjectElement = new ObjectElement(
			"GradientGlowFilter", GradientGlowFilter,
			gradientFilterAttributes,
			{},
			"entries",
			gradientEntryGroup);
		
		public static const filterElementGroup:ObjectElementGroup = new ObjectElementGroup([
			dropShadowFilterType,
			blurFilterType,
			bevelFilterType,
			gradientBevelFilterType,
			glowFilterType,
			gradientGlowFilterType,
			colorMatrixFilterType
		]);
		
		public static const transformElements:Object = 
			{
				transform: transformType,
				filters: new ArrayReference(filterElementGroup)
			};
		
		//--------------------------------------
		// segments
		//--------------------------------------
		
		public static const segmentAttributes:Object =
			{
				x: NUMBER,
				y: NUMBER
			};
		
		public static const controlPointAttributes:Object =
			{
				x: NUMBER,
				y: NUMBER,
				control1X: NUMBER,
				control1Y: NUMBER,
				control2X: NUMBER,
				control2Y: NUMBER
			};
		
		public static const moveSegmentType:ObjectElement = new ObjectElement(
			"MoveSegment", Object,
			segmentAttributes);
		
		public static const lineSegmentType:ObjectElement = new ObjectElement(
			"LineSegment", Object,
			segmentAttributes);
		
		public static const closeSegmentType:ObjectElement = new ObjectElement(
			"CloseSegment", Object,
			{});
		
		public static const quadraticBezierSegmentType:ObjectElement = new ObjectElement(
			"QuadraticBezierSegment", Object,
			[
				segmentAttributes,
				controlPointAttributes
			]);
		
		public static const cubicBezierSegmentType:ObjectElement = new ObjectElement(
			"CubicBezierSegment", Object,
			[
				segmentAttributes,
				controlPointAttributes
			]);
		
		public static const segmentTypes:ArrayReference = new ArrayReference(new ObjectElementGroup(
			[
				moveSegmentType,
				lineSegmentType,
				closeSegmentType,
				quadraticBezierSegmentType,
				cubicBezierSegmentType
			]));
		
		//--------------------------------------
		// shapes
		//--------------------------------------
		
		public static const maskElementTypes:ObjectElementGroup = new ObjectElementGroup([]);
		
		public static const maskElements:Object =
			{
				mask: maskElementTypes
			};
		
		public static const rectType:ObjectElement = new ObjectElement(
			"Rect", Rect,
			[
				transformAttributes,
				sizeAttributes,
				renderAttributes,
				{
					radiusX: NUMBER,
					radiusY: NUMBER
				}
			],
			[
				maskElements,
				fillChildElements,
				transformElements
			]);
		
		public static const ellipseType:ObjectElement = new ObjectElement(
			"Ellipse", Ellipse,
			[
				transformAttributes,
				sizeAttributes,
				renderAttributes
			],
			[
				maskElements,
				fillChildElements,
				transformElements
			]);
		
		public static const lineType:ObjectElement = new ObjectElement(
			"Line", Line,
			[
				transformAttributes,
				renderAttributes,
				{
					xFrom: NUMBER,
					yFrom: NUMBER,
					xTo: NUMBER,
					yTo: NUMBER
				}
			],
			[
				maskElements,
				transformElements,
				{
					stroke: strokeTypes
				}
			]);
		
		//--------------------------------------
		// text
		//--------------------------------------
		
		// just set the attributes to IGNORE, since the new text engine's parser will handle it
		
		public static const characterAttributes:Object =
			{
				fontFamily: IGNORE,
				fontSize: IGNORE,
				fontWeight: IGNORE,
				fontStyle: IGNORE, 
				lineHeight: IGNORE,
				tracking: IGNORE,
				textDecoration: IGNORE,
				color: IGNORE, 
				textAlpha: IGNORE,
				whiteSpaceCollapse: IGNORE,
				lineBreak: IGNORE,
				kerning: IGNORE
			};
		
		public static const paragraphAttributes:Object =
			{
				textAlign: IGNORE,
				textAlignLast: IGNORE,
				textIndent: IGNORE,
				marginLeft: IGNORE,
				marginRight: IGNORE, 
				marginTop: IGNORE,
				marginBotton: IGNORE,
				blockProgression: IGNORE,
				direction: IGNORE
			};
		
		public static const paddingAttributes:Object =
			{
				paddingLeft: IGNORE,
				paddingRight: IGNORE,
				paddingTop: IGNORE,
				paddingBottom: IGNORE
			};
		
		public static const textGraphicType:ObjectElement = new ObjectElement(
			"TextGraphic", RichText,
			[
				transformAttributes,
				renderAttributes,
				sizeAttributes,
				characterAttributes,
				paragraphAttributes,
				paddingAttributes
			],
			[
				transformElements,
				{
					content: FXGDecoder.fromXMLTextGraphicTag
				}
			]);
		
		// what about maskType and visible?  Need to add transformX, transformY to TextGraphic
		
		//--------------------------------------
		// bitmaps
		//--------------------------------------
		
		public static const bitmapGraphicType:ObjectElement = new ObjectElement(
			"BitmapGraphic", BitmapImage,
			[
				transformAttributes,
				renderAttributes,
				sizeAttributes,
				{
					source: EMBED,
					repeat: BOOLEAN
				}
			],
			[
				maskElements,
				transformElements
			]);
		
		//--------------------------------------
		// paths
		//--------------------------------------
		
		public static const pathType:ObjectElement = new ObjectElement(
			"Path",Path,
			[
				transformAttributes,
				renderAttributes,
				{
					closed: BOOLEAN,
					winding: STRING,
					data: STRING
				}
			],
			[
				maskElements,
				transformElements,
				fillChildElements,
				{
					segments: segmentTypes
				}
			]);
		
		//--------------------------------------
		// graphic and groups
		//--------------------------------------
		
		public static const scaleGridAttributes:Object =
			{
				scaleGridLeft: NUMBER,
				scaleGridTop: NUMBER,
				scaleGridRight: NUMBER,
				scaleGridBottom: NUMBER
			};
		
		public static const graphicElementTypes:ObjectElementGroup = new ObjectElementGroup([]);
		
		public static const groupDefinitionType:ObjectElement = new ObjectElement(
			"Group", Group,
			[
				scaleGridAttributes
			],
			{},
			"mxmlContent",
			graphicElementTypes);
		
		public static const groupInlineType:ObjectElement = new ObjectElement(
			"Group", Group,
			[
				scaleGridAttributes,
				transformAttributes,
				renderAttributes
			],
			[
				transformElements,
				maskElements
			],
			"mxmlContent",
			graphicElementTypes);
		
		public static const placeObjectType:ObjectElement = new ObjectElement(
			"Group", Group,
			[
				transformAttributes,
				renderAttributes,
				maskElements
			],
			transformElements);
		
		graphicElementTypes.legalTypes =
			[
				rectType,
				ellipseType,
				groupInlineType,
				pathType,
				bitmapGraphicType,
				textGraphicType,
				lineType
			];
		
		maskElementTypes.legalTypes =
			[
				groupInlineType
			];
		
		public static const graphicType:ObjectElement = new ObjectElement(
			"Graphic",Graphic,
			[
				scaleGridAttributes,
				{
					viewWidth:NUMBER,
					viewHeight:NUMBER,
					version:NUMBER,
					maskType: STRING
				}
			],
			[
				maskElements,
				{
					Private: FXGDecoder.skipElement,
					Library: FXGDecoder.fromXMLLibrary
				}
			],
			"mxmlContent",
			graphicElementTypes);
	}
}